# syntax=docker/dockerfile:1.19

# Hocuspocus realtime-collaboration server image.
#
# Built from the monorepo root (build context = repository root) so the
# Docker layer cache can reuse pnpm install layers shared with the api /
# worker / web images. Final image is the pinned Node 24 Bookworm image running
# `apps/hocuspocus/dist/index.js` on port 1234.
FROM --platform=linux/amd64 node:24.18.0-bookworm-slim@sha256:0778d035a13f3f3833b7f2cb750e0df6cbce45583e84fd822f499f0c902a6c74 AS base

ENV NODE_ENV="production"
ENV CI="true"
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

RUN apt-get update \
  && apt-get install -y --no-install-recommends openssl \
  && rm -rf /var/lib/apt/lists/* \
  && corepack enable \
  && corepack prepare pnpm@11.12.0 --activate \
  && rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx

FROM base AS deps
WORKDIR /myapp
ADD package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/api/package.json apps/api/package.json
COPY apps/hocuspocus/package.json apps/hocuspocus/package.json
COPY apps/mcp/package.json apps/mcp/package.json
COPY apps/portal-domain-controller/package.json apps/portal-domain-controller/package.json
COPY apps/web/package.json apps/web/package.json
COPY apps/worker/package.json apps/worker/package.json
COPY packages/activity/package.json packages/activity/package.json
COPY packages/api-client-ts/package.json packages/api-client-ts/package.json
COPY packages/api-types/package.json packages/api-types/package.json
COPY packages/brand/package.json packages/brand/package.json
COPY packages/billing/package.json packages/billing/package.json
COPY packages/captcha/package.json packages/captcha/package.json
COPY packages/chat/package.json packages/chat/package.json
COPY packages/cli/package.json packages/cli/package.json
COPY packages/config/package.json packages/config/package.json
COPY packages/db/package.json packages/db/package.json
COPY packages/email-sequences/package.json packages/email-sequences/package.json
COPY packages/email-templates/package.json packages/email-templates/package.json
COPY packages/entitlements/package.json packages/entitlements/package.json
COPY packages/error-reporter/package.json packages/error-reporter/package.json
COPY packages/exports/package.json packages/exports/package.json
COPY packages/feature-flags/package.json packages/feature-flags/package.json
COPY packages/form-widget/package.json packages/form-widget/package.json
COPY packages/foundation/package.json packages/foundation/package.json
COPY packages/i18n/package.json packages/i18n/package.json
COPY packages/imports/package.json packages/imports/package.json
COPY packages/integrations/package.json packages/integrations/package.json
COPY packages/logger/package.json packages/logger/package.json
COPY packages/mail/package.json packages/mail/package.json
COPY packages/marketing-outbound/package.json packages/marketing-outbound/package.json
COPY packages/metrics/package.json packages/metrics/package.json
COPY packages/observability/package.json packages/observability/package.json
COPY packages/plugin-runtime/package.json packages/plugin-runtime/package.json
COPY packages/plugin-sdk/package.json packages/plugin-sdk/package.json
COPY packages/queue/package.json packages/queue/package.json
COPY packages/realtime/package.json packages/realtime/package.json
COPY packages/search/package.json packages/search/package.json
COPY packages/shared-store/package.json packages/shared-store/package.json
COPY packages/shared/package.json packages/shared/package.json
COPY packages/storage/package.json packages/storage/package.json
COPY packages/ui-app/package.json packages/ui-app/package.json
COPY packages/ui-mock/package.json packages/ui-mock/package.json
COPY packages/ui-subframe/package.json packages/ui-subframe/package.json
COPY packages/ui/package.json packages/ui/package.json
COPY packages/url/package.json packages/url/package.json
COPY packages/view-models/package.json packages/view-models/package.json
COPY runtime/api/package.json runtime/api/package.json
COPY runtime/hocuspocus/package.json runtime/hocuspocus/package.json
COPY runtime/mcp/package.json runtime/mcp/package.json
COPY runtime/portal-domain-controller/package.json runtime/portal-domain-controller/package.json
COPY runtime/web/package.json runtime/web/package.json
COPY runtime/worker/package.json runtime/worker/package.json
COPY patches ./patches
RUN --mount=type=cache,id=hivegrid-hocuspocus-pnpm-store,target=/pnpm/store,sharing=locked \
  pnpm fetch --frozen-lockfile
RUN --mount=type=cache,id=hivegrid-hocuspocus-pnpm-store,target=/pnpm/store,sharing=locked \
  pnpm install --frozen-lockfile --prod=false --offline

FROM base AS build
ENV DATABASE_URL="postgresql://hivegrid:hivegrid@localhost:5432/hivegrid?schema=public"
WORKDIR /myapp
COPY --from=deps /myapp/node_modules /myapp/node_modules
COPY --from=deps /myapp/apps /myapp/apps
COPY --from=deps /myapp/packages /myapp/packages
ADD package.json pnpm-lock.yaml pnpm-workspace.yaml ./
ADD prisma prisma
ADD prisma.config.ts prisma.config.ts
ADD tsconfig.json tsconfig.json
ADD packages/db packages/db
ADD . .
# Dependencies were already resolved and policy-checked in the deps stage.
# Invoke the pinned Prisma binary directly to avoid pnpm's workspace-wide
# resolution pass on every source-only rebuild. Hocuspocus bundles the DB
# client source and does not ship the database package's seed artifacts.
RUN ./node_modules/.bin/prisma generate --config prisma.config.ts
RUN node apps/hocuspocus/scripts/build.mjs

FROM build AS hocuspocus-runtime-artifacts
RUN find /myapp/apps/hocuspocus/dist -type f -name '*.map' -delete

FROM deps AS hocuspocus-production-deps
RUN --mount=type=cache,id=hivegrid-hocuspocus-pnpm-store,target=/pnpm/store,sharing=locked \
  pnpm fetch --frozen-lockfile \
  && pnpm --offline --config.inject-workspace-packages=true --filter @hivegrid/runtime-hocuspocus deploy --prod --no-optional /prod/hocuspocus
COPY --from=build /myapp/packages/db/generated/prisma/client/internal/class.ts \
  /myapp/packages/db/generated/prisma/client/internal/class.ts
COPY scripts/prune-prisma-client-runtime.mjs /myapp/scripts/prune-prisma-client-runtime.mjs
RUN node /myapp/scripts/prune-prisma-client-runtime.mjs \
  /prod/hocuspocus/node_modules /myapp/packages/db/generated/prisma/client/internal/class.ts
COPY scripts/generate-sbom.mjs /myapp/scripts/generate-sbom.mjs
RUN SBOM_TARGET=hocuspocus SBOM_ROOT_PATH=/prod/hocuspocus SBOM_NODE_MODULES_PATH=/prod/hocuspocus/node_modules \
  SBOM_OUTPUT_PATH=/prod/hocuspocus/sbom-pnpm.json node /myapp/scripts/generate-sbom.mjs

FROM base AS hocuspocus
ARG APP_REVISION=unknown
ENV NODE_ENV="production"
ENV HOCUSPOCUS_PORT="1234"
ENV HOCUSPOCUS_HOST="0.0.0.0"
ENV APP_REVISION="${APP_REVISION}"
LABEL org.opencontainers.image.revision="${APP_REVISION}"
LABEL org.opencontainers.image.sbom="/myapp/sbom-pnpm.json"
EXPOSE 1234

WORKDIR /myapp
COPY --exclude=**/*.map --exclude=**/*.d.ts --from=hocuspocus-production-deps /prod/hocuspocus/node_modules /myapp/node_modules
COPY --from=hocuspocus-production-deps /prod/hocuspocus/sbom-pnpm.json /myapp/sbom-pnpm.json
COPY --from=build /myapp/apps/hocuspocus/package.json /myapp/apps/hocuspocus/package.json
COPY --from=hocuspocus-runtime-artifacts /myapp/apps/hocuspocus/dist /myapp/apps/hocuspocus/dist

# The collaboration runtime never installs or builds packages. Remove package
# manager shims inherited from the build base to reduce executable surface.
RUN rm -rf /root/.cache/node/corepack /usr/local/lib/node_modules/corepack /opt/yarn-* \
  /usr/local/bin/corepack /usr/local/bin/pnpm /usr/local/bin/pnpx \
  /usr/local/bin/yarn /usr/local/bin/yarnpkg

USER node

HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --start-interval=2s --retries=3 \
  CMD node -e "require('net').connect(${HOCUSPOCUS_PORT:-1234}, '127.0.0.1').on('connect', () => process.exit(0)).on('error', () => process.exit(1))"

CMD ["node", "apps/hocuspocus/dist/index.js"]
